﻿// 
// Project: Soapi.JS
// http://soapijs.codeplex.com
// http://stackapps.com/questions/494
// 
// Copyright 2010, Sky Sanders
// Dual licensed under the MIT or GPL Version 2 licenses.
// http://soapi.info/code/js/license.aspx
// 
// Date: July 2 2010 
// Version: RC2.1 - API Version 0.9 Revision 2010.06.29.001
// 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Ionic.Zip;
using Ionic.Zlib;
using Microsoft.Ajax.Utilities;

namespace Soapi.JS.Deploy
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            string releaseVersion = args[0];
            string apiVersion = args[1];
            string apiRevision = args[2];
            string scriptVersion = releaseVersion + " " + apiVersion + " " + apiRevision;

            string rootPath = Path.GetDirectoryName(Environment.CurrentDirectory);
            string srcPath = Path.Combine(rootPath, "src");
            string srcScriptsPath = Path.Combine(srcPath, "scripts");
            string srcThirdPartyPath = Path.Combine(srcPath, "3rd-party");
            ;
            string buildPath = Path.Combine(rootPath, "build");
            string buildScriptsPath = Path.Combine(buildPath, "scripts");
            string buildThirdPartyPath = Path.Combine(buildPath, "3rd-party");
            ;
            string downloadsPath = Path.Combine(rootPath, "downloads");
            string jsDocPath = Path.Combine(rootPath, "jsdoc-toolkit");
            string docsOutputPath = Path.Combine(buildPath, "docs");
            var scriptSrcPath = Path.Combine(srcScriptsPath, "Soapi.js");
            var vsDocSrcPath = Path.Combine(srcScriptsPath, "Soapi-vsdoc.js");

            var script = File.ReadAllText(scriptSrcPath);
            var vsDocScript = File.ReadAllText(vsDocSrcPath);


            BuildOutputAndStaticContent(srcPath, srcScriptsPath, buildPath, buildScriptsPath, srcThirdPartyPath,
                                        buildThirdPartyPath);

            UpdateVersionAndDate(scriptVersion, buildScriptsPath, scriptSrcPath, vsDocSrcPath, ref script,
                                 ref vsDocScript, apiVersion, apiRevision);

            WriteVsDocs(buildScriptsPath, vsDocScript);

            CompressScript(buildScriptsPath, script);

            GenerateDocs(jsDocPath, docsOutputPath, vsDocSrcPath);

            PostProcessDocs(docsOutputPath, script);

            BuildDownloadFiles(scriptVersion, buildPath, buildScriptsPath, downloadsPath, docsOutputPath);
        }

        private static void BuildOutputAndStaticContent(string srcPath, string srcScriptsPath, string buildPath,
                                                        string buildScriptsPath, string srcThirdPartyPath,
                                                        string buildThirdPartyPath)
        {
            // prepare buildPath)
            if (Directory.Exists(buildThirdPartyPath))
            {
                Directory.Delete(buildThirdPartyPath, true);
            }
            if (Directory.Exists(buildScriptsPath))
            {
                Directory.Delete(buildScriptsPath, true);
            }
            if (Directory.Exists(buildPath))
            {
                Directory.Delete(buildPath, true);
            }
            Directory.CreateDirectory(buildPath);
            Directory.CreateDirectory(buildScriptsPath);
            Directory.CreateDirectory(buildThirdPartyPath);
            // copy third party scripts
            foreach (string file in Directory.GetFiles(srcThirdPartyPath, "*.*"))
            {
                File.Copy(file, Path.Combine(buildThirdPartyPath, Path.GetFileName(file)));
            }
            // copy content
            foreach (string file in Directory.GetFiles(srcPath, "*.htm*"))
            {
                File.Copy(file, Path.Combine(buildPath, Path.GetFileName(file)));
            }
            // copy license
            foreach (string file in Directory.GetFiles(srcScriptsPath, "*.txt"))
            {
                File.Copy(file, Path.Combine(buildScriptsPath, Path.GetFileName(file)));
                File.Copy(file, Path.Combine(buildPath, Path.GetFileName(file)));
            }
            // copy scripts
            foreach (string file in Directory.GetFiles(srcScriptsPath, "*.js"))
            {
                File.Copy(file, Path.Combine(buildScriptsPath, Path.GetFileName(file)));
            }

        }

        private static void UpdateVersionAndDate(string scriptVersion, string buildScriptsPath, string scriptSrcPath,
                                                 string vsDocSrcPath, ref string script, ref string vsDocScript, string apiVersion, string apiRevision)
        {
            // update version and date in src

            script = Regex.Replace(script, @"version\s*=\s*'[^']*';", "version = '" + scriptVersion + "';");
            script = Regex.Replace(script, @"apiVersion\s*=\s*'[^']*';", "apiVersion = '" + apiVersion + "';");
            script = Regex.Replace(script, @"apiRevision\s*=\s*'[^']*';", "apiRevision = '" + apiRevision + "';");

            vsDocScript = Regex.Replace(vsDocScript, @"version\s*=\s*'[^']*';", "version = '" + scriptVersion + "';");
            vsDocScript = Regex.Replace(vsDocScript, @"apiVersion\s*=\s*'[^']*';", "apiVersion = '" + apiVersion + "';");
            vsDocScript = Regex.Replace(vsDocScript, @"apiRevision\s*=\s*'[^']*';", "apiRevision = '" + apiRevision + "';");



            script = Regex.Replace(script, "// Date:.*", "// Date: " + DateTime.Now.ToUniversalTime());
            script = Regex.Replace(script, "// Version:.*", "// Version: " + scriptVersion);
            File.WriteAllText(scriptSrcPath, script);
            File.WriteAllText(Path.Combine(buildScriptsPath, "Soapi.js"), script);

            vsDocScript = Regex.Replace(vsDocScript, "// Date:.*", "// Date: " + DateTime.Now.ToUniversalTime());
            vsDocScript = Regex.Replace(vsDocScript, "// Version:.*", "// Version: " + scriptVersion);
            vsDocScript = Regex.Replace(vsDocScript, "\\* @version.*", "* @version " + scriptVersion);
            File.WriteAllText(vsDocSrcPath, vsDocScript);
        }

        private static void WriteVsDocs(string buildScriptsPath, string vsDocScript)
        {
            var vsDoc = Regex.Replace(vsDocScript, @"^\s*/\*\*.*?\*/\s*$", "",
                                      RegexOptions.Singleline | RegexOptions.Multiline);
            File.WriteAllText(Path.Combine(buildScriptsPath, "Soapi-vsdoc.js"), vsDoc);
            File.WriteAllText(Path.Combine(buildScriptsPath, "Soapi.min-vsdoc.js"), vsDoc);
        }

        private static void CompressScript(string buildScriptsPath, string script)
        {
            var settings = new CodeSettings
                               {
                                   MinifyCode = true,
                                   OutputMode = OutputMode.SingleLine,
                                   CollapseToLiteral = false,
                                   CombineDuplicateLiterals = true,
                                   EvalTreatment = EvalTreatment.Ignore,
                                   IndentSize = 0,
                                   InlineSafeStrings = false,
                                   LocalRenaming = LocalRenaming.CrunchAll,
                                   MacSafariQuirks = true,
                                   PreserveFunctionNames = true,
                                   RemoveFunctionExpressionNames = true,
                                   RemoveUnneededCode = false,
                                   StripDebugStatements = true
                               };
            var scriptMin = String.Format("{0}\r\n{1}", script.Substring(0, script.IndexOf("//----------")),
                                          new Minifier().MinifyJavaScript(script, settings));
            string scriptMinPath = Path.Combine(buildScriptsPath, "Soapi.min.js");
            File.WriteAllText(scriptMinPath, scriptMin);

            //gzip the minified script);
            using (var streamOut = new MemoryStream())
            {
                using (
                    GZipStream gzString = new GZipStream(streamOut, CompressionMode.Compress, CompressionLevel.Level9))
                {
                    var buff = Encoding.UTF8.GetBytes(scriptMin);
                    gzString.Write(buff, 0, buff.Length);
                }
                File.WriteAllBytes(scriptMinPath + ".gz", streamOut.ToArray());
            }
        }

        private static void GenerateDocs(string jsDocPath, string docsOutputPath, string vsDocSrcPath)
        {
            // generated the docs

            if (Directory.Exists(docsOutputPath))
            {
                Directory.Delete(docsOutputPath, true);
            }
            Directory.CreateDirectory(docsOutputPath);

            Environment.CurrentDirectory = jsDocPath;

            var processInfo = new ProcessStartInfo("java.exe",
                                                   string.Format(
                                                       @"-jar jsrun.jar app/run.js ""{0}"" -d=""{1}"" -t=templates/jsdoc",
                                                       vsDocSrcPath, docsOutputPath))
                                  {
                                      CreateNoWindow = true,
                                      UseShellExecute = false,
                                      WorkingDirectory = jsDocPath
                                  };
            Process proc;

            if ((proc = Process.Start(processInfo)) == null)
            {
                throw new InvalidOperationException("??");
            }

            proc.WaitForExit();
            int exitCode = proc.ExitCode;
            proc.Close();
        }

        private static void PostProcessDocs(string docsOutputPath, string script)
        {
            // post process the docs until i can take the time to dig in and customize the generation engine

            var enums = new[]
                            {
                                "SortOrder", "PostSort", "CommentSort", "QuestionSort", "TagSort", "UserSort",
                                "FavoritesSort", "SiteState", "UserType", "PostType", "PostTimelineType", "RevisionType"
                                , "UserTimelineType"
                            };


            var responseTypes = new[]
                                    {
                                        "AnswersResponse", "BadgesResponse", "CommentsResponse", "ErrorsResponse",
                                        "PostTimelineResponse", "QuestionsResponse", "RepChangesResponse",
                                        "RevisionsResponse", "StatsResponse", "TagsResponse", "UserTimelinesResponse",
                                        "UsersResponse", "SitesResponse", "UsersAssociatedResponse"
                                    };

            // delete the source file that jsdoc saves
            string sourceFileName = Directory.GetFiles(Path.Combine(docsOutputPath, "symbols\\src"))[0];
            File.Delete(sourceFileName);
            sourceFileName = Path.GetFileName(sourceFileName);
            File.WriteAllText(Path.Combine(docsOutputPath, "symbols\\src\\soapi.js.txt"), script);


            List<string> files =
                new List<string>(Directory.GetFiles(docsOutputPath, "*.html", SearchOption.AllDirectories));

            foreach (string file in files)
            {
                string fileText = File.ReadAllText(file);

                fileText = fileText.Replace(sourceFileName, "soapi.js.txt");
                fileText = fileText.Replace("Soapi-vsdoc.js", "Soapi.js");

                foreach (string name in enums)
                {
                    fileText = fileText.Replace("Class Soapi.Domain." + name, "Enum Soapi.Domain." + name);
                }

                // jsdoc does not like the func params. we need to fix that

                fileText = fileText.Replace("|context", ", context");

                foreach (string responseType in responseTypes)
                {
                    fileText = fileText.Replace(string.Format("func(Soapi.Responses.{0}", responseType),
                                                string.Format(
                                                    "func(<a href='Soapi.Responses.{0}.html'>Soapi.Responses.{0}</a>",
                                                    responseType));

                    fileText = fileText.Replace(string.Format("func({0}", responseType),
                            string.Format(
                                "func(<a href='Soapi.Responses.{0}.html'>{0}</a>",
                                responseType));
                }

                // add a header. I am sure there is a tag for this but I cannot find it
                fileText = fileText.Replace("<div id=\"header\">",
                                            "<div id=\"header\"><h1 class='classTitle'>Soapi.js</h1>");


                File.WriteAllText(file, fileText);
            }
        }

        private static void BuildDownloadFiles(string scriptVersion, string buildPath, string buildScriptsPath,
                                               string downloadsPath, string docsOutputPath)
        {
            Directory.CreateDirectory(downloadsPath);

            using (var zipFile = new ZipFile())
            {
                zipFile.CompressionLevel = CompressionLevel.Level9;
                zipFile.AddDirectory(docsOutputPath);
                string zipFileName = String.Format("Soapi.js {0} docs only.zip", scriptVersion);
                if (File.Exists(zipFileName))
                {
                    File.Delete(zipFileName);
                }
                zipFile.Save(Path.Combine(downloadsPath, zipFileName));
            }

            using (var zipFile = new ZipFile())
            {
                zipFile.CompressionLevel = CompressionLevel.Level9;
                zipFile.AddDirectory(buildScriptsPath);
                string zipFileName = String.Format("Soapi.js {0} scripts only.zip", scriptVersion);
                if (File.Exists(zipFileName))
                {
                    File.Delete(zipFileName);
                }
                zipFile.Save(Path.Combine(downloadsPath, zipFileName));
            }

            using (var zipFile = new ZipFile())
            {
                zipFile.CompressionLevel = CompressionLevel.Level9;
                zipFile.AddDirectory(buildPath);
                string zipFileName = String.Format("Soapi.js {0} all.zip", scriptVersion);
                if (File.Exists(zipFileName))
                {
                    File.Delete(zipFileName);
                }
                zipFile.Save(Path.Combine(downloadsPath, zipFileName));
            }
        }
    }
}